home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr10 / gsar106.zip / GSAR.C < prev    next >
C/C++ Source or Header  |  1993-06-01  |  30KB  |  917 lines

  1. /* gsar.c ***************************************** UPDATED: 930414.20:39 TT
  2.  *
  3.  * Description : General Search And Replace utility ( GSAR ! )
  4.  * Author      : Tormod Tjaberg & Hans Peter Verne
  5.  *
  6.  * Copyright (C) 1992-93 Tormod Tjaberg & Hans Peter Verne,
  7.  * This is free software, distributed under the terms of the
  8.  * GNU General Public License. For details see the file COPYING
  9.  *
  10.  *
  11.  * Changes:
  12.  * 930328 : 1.06 GetArgs now uses GetOpt, context length can be specified on
  13.  *          the command line. Setvbuf error is no longer fatal.
  14.  * 930127 : 1.05 filter implemented, setvbuf used to speed up disk I/O, 
  15.  *          + bugfixes, see gsarbmg.c
  16.  * 921122 : 1.04 implemented signal handling.
  17.  * 920610 : 1.03 correct filename is now passed in OneSearchReplace
  18.  * 920404 : 1.02 BMG routines use compile time generated table instead of
  19.  *          filling it in at run time
  20.  * 920314 : simplified file buffer read --> 1 argument less, ta' HPV
  21.  * 920310 : BMG search & replace now functioning speed increase by 300%
  22.  *          in search and replace and nearly 700% in searching!!
  23.  * 920226 : temporary name is now really temporary
  24.  * 920223 : now able to enter hexadecimal numbers on the command line, UD
  25.  *          DU parameters working.
  26.  * 920203 : main program working all parsing seems to work
  27.  *
  28.  * Currently compiles under :
  29.  * Turbo C 2.0, Turbo C++ 1.0, Turbo C++ 3.0, Zortech C++ 3.0,
  30.  * Watcom C 386 8.0, Ultrix ANSI C, Microsoft 6.0, GCC
  31.  */
  32.  
  33. #include <stdio.h>
  34. #include <stdlib.h>
  35. #include <string.h>
  36. #include <ctype.h>
  37. #include <stdarg.h>
  38. #include <limits.h>
  39. #include <signal.h>
  40. #include <sys/types.h>
  41. #include <sys/stat.h>
  42. #include "comp_dep.h"
  43. #include "arg_func.h"
  44. #include "gsar.h"
  45.  
  46.  
  47. /* Need setmode to turn my streams into binary Under MS-DOS
  48.  */
  49. #ifdef MSDOS
  50. #include <fcntl.h>
  51. #include <io.h>
  52. #endif
  53.  
  54.  
  55. /* Clever little option mimic wildcard expansion from
  56.  * shell ONLY available with the Zortech compiler ver 3.0x and upwards
  57.  */
  58. #if defined(__ZTC__)
  59. #include <dos.h>
  60. WILDCARDS
  61. #endif
  62.  
  63. /* function prototypes internal to this module */
  64.  
  65. void ShowLicence( void );
  66. void Abort( char *, ... );
  67. void CtrlBreak(int );
  68. char *DupStr( char *);
  69. char *ExtractPathFromFSpec( char * );
  70. char *TmpName(char *, char *);
  71. void DumpBuffer( char *, unsigned short, unsigned char);
  72. unsigned short GetPattern(char *, char *);
  73. int GetArgs( int , char *[]);
  74. void StreamSearchReplace( void );
  75. void FileSearch( void );
  76. void OneSearchReplace( void );
  77. void SearchReplace( void );
  78. int main( int , char *[]);
  79.  
  80. /* Macro to determine if char is a path or drive delimiter
  81.  */
  82. #define IS_PATH_DELIM(c)   ((c) == '\\' || (c) == ':' || (c) == '/')
  83.  
  84. #define LF  0xA
  85. #define CR  0xD
  86.  
  87. #define INP_BUF_SIZ  0x4000      /* Input buffer used by setvbuf */
  88.  
  89. unsigned short nItemsSearch = 0;
  90. unsigned short nItemsReplace = 0;
  91.  
  92. /* if the flag below is set we are not allowed to interrupt !
  93.  */
  94. static sig_atomic_t fCriticalPart = 0;
  95.  
  96. OUTPUT_CTRL Ctrl;
  97.  
  98. char  **pFileList;          /* list of files found on the command line */
  99. char  *pOutFile = NULL;     /* current output file name make sure   */
  100.                             /* it's NULL so we won't delete garbage */
  101.  
  102. char  SearchBuf[PAT_BUFSIZ]  = "";
  103. char  ReplaceBuf[PAT_BUFSIZ]  = "";
  104.  
  105. char  fSearchReplace = 0;  /* default to a search initially */
  106. char  fFolded        = 0;  /* fold pattern ie. ignore case */
  107. char  fOverWrite     = 0;  /* overwrite input file */
  108. char  fForce         = 0;  /* force overwrite of existing output file */
  109. char  fBuffers       = 0;  /* just display search & replace buffers */
  110. char  fFilter        = 0;  /* make GSAR act like a filter */
  111.  
  112. /* Usage text and GNU licence information 
  113.  */
  114.  
  115. char  *Usage[] =
  116. {
  117.    "gsar ver 1.06 -- Copyright (C) 1992-93 Tormod Tjaberg & Hans Peter Verne",
  118.    "",
  119.    "Usage: gsar [options] [infile(s)] [outfile]",
  120.    "Options are:",
  121.    "-s<string> Search string ",
  122.    "-r[string] Replace string. Use '-r' to delete the search string from the file",
  123.    "-i         Ignore case difference when comparing strings",
  124.    "-B         just display search & replace Buffers",
  125.    "-f         Force overwrite of an existing output file",
  126.    "-o         Overwrite the existing input file",
  127.    "-c[n]      show textual Context of match, 'n' is number of bytes in context",
  128.    "-x[n]      show context as a heX dump, 'n' is number of bytes in context",
  129.    "-b         display Byte offsets of matches in file",
  130.    "-l         only List filespec and number of matches ( default )",
  131.    "-h         suppress display of filespec when displaying context or offsets",
  132.    "-du        convert a DOS ASCII file to UNIX ( strips carriage return )",
  133.    "-ud        convert a UNIX ASCII file to DOS ( adds carriage return )",
  134.    "-F         'Filter' mode, input from stdin and eventual output to stdout",
  135.    "-G         display the GNU General Public Licence",
  136.    "",
  137.    "Ctrl characters can be entered by using a ':' in the string followed by the",
  138.    "ASCII value of the character. The value is entered using ':' followed by three",
  139.    "decimal digits or ':x' followed by two hex numbers. To enter ':' use '::'",
  140.    NULL
  141. };         
  142.  
  143. char  *Licence[] =
  144. {
  145.    "This program is free software; you can redistribute it and/or modify",
  146.    "it under the terms of the GNU General Public License as published by",
  147.    "the Free Software Foundation; either version 1, or (at your option)",
  148.    "any later version.",
  149.    "",
  150.    "This program is distributed in the hope that it will be useful,",
  151.    "but WITHOUT ANY WARRANTY; without even the implied warranty of",
  152.    "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the",
  153.    "GNU General Public License for more details.",
  154.    "",
  155.    "You should have received a copy of the GNU General Public License",
  156.    "along with this program; if not, write to the Free Software",
  157.    "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.",
  158.    NULL
  159. };
  160.  
  161. /* ShowLicence ()
  162.  *
  163.  * Display the GNU General Public License
  164.  */
  165. void ShowLicence( void )
  166. {
  167.    int i = 0;
  168.  
  169.    while (Licence[i] != NULL)
  170.    {
  171.       fputs( Licence[i++], Ctrl.fpMsg);
  172.       fputc( '\n', Ctrl.fpMsg );
  173.    }
  174. }
  175.  
  176.  
  177. /* Abort ()
  178.  *
  179.  * Input  : Message to be displayed with the format of standard printf!
  180.  * Returns: Nothing, but instead it exits to operating system
  181.  *
  182.  * Terminate program, return to OS with a suitable return value
  183.  * exit() will close all open files. All fatal errors are written
  184.  * to stderr.
  185.  */
  186. void Abort( char *pMessage, ... )
  187. {
  188.    va_list argp;
  189.  
  190.    fprintf( stderr, "gsar: ");
  191.    va_start(argp, pMessage);
  192.    vfprintf( stderr ,pMessage, argp);
  193.    va_end(argp);
  194.    fprintf( stderr, "\n");
  195.    exit(EXIT_FAILURE);  /* exit & tell operating system that we've failed */
  196. }
  197.  
  198.  
  199. /* CtrlBreak ()
  200.  *
  201.  * Ctrl-Break handler. Returns to operating system
  202.  */
  203. void CtrlBreak(int Val)
  204. {
  205.    if ( fCriticalPart )
  206.       return;
  207.  
  208.    signal(SIGINT,SIG_IGN);
  209.  
  210.    /* Before we die try to clean up as much as possible
  211.     * make sure we don't delete stdout...
  212.     */
  213.    if ( fSearchReplace && pOutFile != NULL && !fFilter  )
  214.       remove( pOutFile );
  215.  
  216.    exit(EXIT_FAILURE);  /* exit & tell operating system that we've failed */
  217. }
  218.  
  219.  
  220. /* DupStr()
  221.  *
  222.  * Input  : pActStr - pointer to actual string which is to be duplicated
  223.  * Returns: pDupStr - pointer to malloc'd duplicate
  224.  *          NULL - Out of memory
  225.  *
  226.  * Duplicates a string by malloc'ing and then doing a strcpy
  227.  */
  228. char *DupStr( char *pActStr )
  229. {
  230.    register char *pDupStr;
  231.  
  232.    pDupStr = ( char * ) malloc(strlen(pActStr) + 1);
  233.    if (pDupStr)
  234.       strcpy(pDupStr,pActStr);
  235.    return pDupStr;
  236. }
  237.  
  238. /* ExtractPathFromFSpec()
  239.  *
  240.  * Input  : filespec - a filespec which contains a path and a filename
  241.  * Returns: savfilespec - pointer to malloc'd string with path
  242.  *
  243.  * Extracts the path from a filespec
  244.  */
  245. char *ExtractPathFromFSpec( register char *filespec)
  246. {
  247.    register char *p;
  248.    register char *savfilespec;
  249.  
  250.    savfilespec = DupStr(filespec); /* avoid destroying the original */
  251.  
  252.     /* Start at end of string and back up till we find the beginning */
  253.     /* of the filename or a path.               */
  254.    for ( p = savfilespec + strlen(savfilespec);
  255.          p != savfilespec && !IS_PATH_DELIM(*(p - 1));
  256.          p--) ;
  257.    *p = '\0';
  258.    return savfilespec;
  259. }
  260.  
  261.  
  262. /* TmpName
  263.  *
  264.  * Input  : pPath - which has to be properly terminated
  265.  *          pPrefix - tmp filename prefix maximum 4 chars
  266.  * Returns: pointer to static buffer
  267.  *          NULL - unable to generate tmp file name
  268.  *
  269.  * Generates a temporary filename by combining an optional path,
  270.  * a prefix ,a number between 0 & FFFF and the suffix '.tmp'.
  271.  */
  272. char *TmpName(char *pPath, char *pPrefix)
  273. {
  274.    static char FileSpec[ FILENAME_MAX ];
  275.    static char Seed = 0;
  276.  
  277.    static unsigned short TmpNum;
  278.    char *pDigits;
  279.    unsigned short i;
  280.    struct stat buf;
  281.  
  282.    *FileSpec = '\0'; /* Null terminate the buffer so strcat will always work */
  283.    if ( !Seed )      /* if first time through */
  284.    {
  285.       Seed++;
  286.       TmpNum = 0xFFFF;  /* since we increment this will make the first number 0000 */
  287.    }
  288.  
  289.    if ( pPath != NULL )         
  290.       strcpy( FileSpec,pPath );      /* first the path  */
  291.    if ( pPrefix != NULL )
  292.       strcat( FileSpec,pPrefix );    /* then the prefix */
  293.  
  294.    /* pointer to start of digits in filespec */
  295.    pDigits = FileSpec + strlen( FileSpec );
  296.  
  297.    for ( i = 0 ; i <= 0x7FFF ; i++ )     /* try 0x7FFF times */
  298.    {
  299.       TmpNum++;                          /* TmpNum is static ! */
  300.       sprintf( pDigits ,"%04x",TmpNum ); /* convert to string */
  301.       strcat( FileSpec,".tmp" );         /* add the suffix */
  302.       if ( stat ( FileSpec, &buf) != 0 )
  303.          return FileSpec;                /* file not found, success */
  304.       *( pDigits ) = '\0';               /* pre_XXXX --> pre_ */
  305.    }
  306.    /* unable to create a temporary file ! more than 0x7FFF files !!*/
  307.    return NULL;
  308. }
  309.  
  310. /* DumpBuffer()
  311.  *
  312.  * Input    : pBuffer pointer to character buffer
  313.  *            nItem number of items in buffer
  314.  *            base  1 = hex , 0 = ASCII
  315.  * Returns  : nothing
  316.  *
  317.  * Prints the contents of a buffer in either ASCII or HEX. The
  318.  * nItems variable is needed since we don't stop on a NUL
  319.  */
  320. void DumpBuffer( char *pBuffer, unsigned short nItem , unsigned char Base )
  321. {
  322.    unsigned short i;                 /* loop counter */
  323.  
  324.    if ( !Base )
  325.    {
  326.       for ( i = 0 ; i < nItem ; i++ )
  327.       {
  328. #ifdef MSDOS   /* MSDOS can display all characters except CTRL chars */
  329.          if ( !iscntrl( (int) *pBuffer ) )
  330. #else         /* its __UNIX__ */
  331.          if ( isprint( (int) *pBuffer ))
  332. #endif
  333.             fputc( *pBuffer, Ctrl.fpMsg );
  334.          else
  335.             fputc( '.', Ctrl.fpMsg );
  336.  
  337.          pBuffer++;
  338.       }
  339.    }
  340.    else
  341.       for ( i = 0 ; i < nItem ; i++ )
  342.          fprintf(Ctrl.fpMsg,"%02x ", ( unsigned char ) *pBuffer++);
  343.  
  344.    fputc( '\n',Ctrl.fpMsg );
  345. }
  346.  
  347.  
  348. /* GetPattern()
  349.  *
  350.  * Input    : pArgStr - pointer to command line search or replace string
  351.  *            pBuffer - pointer to buffer to store the parsed string
  352.  * Returns  : actual length of parsed string
  353.  *
  354.  * Takes a string & transforms it into the correct internal representation
  355.  * ie :070OO -> FOO or :x46OO -> FOO
  356.  */
  357. unsigned short GetPattern(char *pArgStr, char *pBuffer)
  358. {
  359.    char  number[4]; /* array to store number to convert */
  360.    char  *pEnd;     /* pointer to the result of the string to long conversion */
  361.    char  *pStart;   /* pointer to the start of the buffer */
  362.  
  363.    pStart = pBuffer;
  364.    number[3] = '\0'; /* make sure number buffer is terminated */
  365.  
  366.    while (*pArgStr != '\0')
  367.    {
  368.       if (*pArgStr != ':')
  369.           *pBuffer++ = *pArgStr++;       /* just copy the buffer */
  370.       else
  371.       {
  372.          if ( *(pArgStr+1) == ':' )      /* check for multiple :'s */
  373.          {
  374.             *pBuffer++ = *pArgStr;
  375.             pArgStr = pArgStr + 2;       /* position ourselves past the 2 :'s */
  376.          }
  377.          else
  378.          {  /* have we actually got three chars to copy ? */
  379.             if ( strlen(++pArgStr) > 2 )  
  380.             {
  381.                memcpy(number,pArgStr,3);  /* negative numbers are silently ignored*/
  382.                pArgStr += 3;              /* jump past this number in the argument string */
  383.                if ( ( char ) tolower( number[0] ) == 'x' )
  384.                {                          /* perform hex conversion */
  385.                   number[0] = '0';        /* replace the x with a zero */
  386.                   *pBuffer++ = ( char ) strtol( number, &pEnd, 16 );
  387.                   if ( pEnd != number+3 )
  388.                   Abort("command error, not a valid hexadecimal number : %s\n",&number[1]);
  389.                }
  390.                else                       /* decimal conversion */
  391.                {
  392.                   *pBuffer++ = ( char ) strtol( number, &pEnd, 10 );
  393.                   if ( pEnd != number+3 )
  394.                   Abort("command error, not a valid decimal number : %s\n",number);
  395.                }
  396.             }
  397.             else                          /* strlen(pArgStr) < 3 so abort */
  398.                Abort("command error, a single colon must be followed by three decimal digits or an 'x' followed by 2 hexadecimal numbers\n");
  399.          }
  400.       }
  401.       if ( pBuffer - pStart > PAT_BUFSIZ )
  402.          Abort("command error, length of search or replace buffer must not exceed %d chars",PAT_BUFSIZ);
  403.    }  /* while */                      
  404.    return pBuffer - pStart;               /* actual length of buffer */
  405. }
  406.  
  407. /* GetArgs()
  408.  *                  
  409.  * Input  : argc - number of arguments on the command line
  410.  *          argv - pointer to the argument vevtor
  411.  * Returns: number of actual filenames found
  412.  *
  413.  * Parses the command line & returns number of filenames found
  414.  */
  415. int GetArgs( int argc, char *argv[])
  416. {
  417.    int   i = 0;
  418.    int   j = 0;          /* counters */
  419.    int   c ;             /* switch char */
  420.    char  *pEnd;          /* the result of the string to long conversion */
  421.    long  longVal;
  422.  
  423.  
  424.    pFileList = NULL;  
  425.  
  426.    if ( argc > 1 )
  427.       while ( (c = GetOpt(argc, argv, "|s:r::iBfoc::x::blhd::u::FG")) != EOF )
  428.       {
  429.          switch ( c )
  430.          {
  431.             case '|':
  432.                /* create a vector of file pointers */
  433.                  pFileList = (char **) realloc ( pFileList, (j + 2) * sizeof( char * ));    
  434.                    pFileList[j++] = pOptArg;
  435.                pFileList[j] = NULL;   /* The C-standard specifies that argv[argc] == NULL */
  436.                    break;
  437.             case MISSING_ARG:
  438.                Abort("command error, the '%c' option requires an argument", (unsigned char) CurOpt);
  439.                break;
  440.             case BAD_CHAR:
  441.                Abort("command error, unknown option '%c'. Type 'gsar' by itself help", ( unsigned char ) CurOpt);
  442.                break;
  443.             case MISSING_OPT:
  444.                i = 0;
  445.                while ( Usage[i] != NULL )
  446.                {
  447.                   fputs( Usage[i++], Ctrl.fpMsg );
  448.                   fputc( '\n', Ctrl.fpMsg );
  449.                }
  450.                exit( EXIT_SUCCESS );
  451.                break;
  452.             case 's':
  453.                nItemsSearch = GetPattern( pOptArg, SearchBuf );
  454.                break;
  455.             case 'r':
  456.                if ( pOptArg == NULL )
  457.                   nItemsReplace = 0;    /* we are to remove occurrence of -s */
  458.                else
  459.                   nItemsReplace = GetPattern( pOptArg, ReplaceBuf );
  460.                fSearchReplace = 1;     /* only search & replace if -r option */
  461.                break;
  462.             case 'u':                  /* replace LF with CR LF */
  463.                if ( pOptArg == NULL )
  464.                   pOptArg = "";
  465.                if ( !(*pOptArg == 'd' && *(pOptArg + 1) == '\0')  )
  466.                   Abort("command error, unknown option 'u%s'. Type 'gsar' by itself for help", pOptArg);
  467.                fSearchReplace = 1;
  468.                nItemsSearch = 1;
  469.                SearchBuf[0] = LF;
  470.                nItemsReplace = 2;
  471.                ReplaceBuf[0] = CR;
  472.                ReplaceBuf[1] = LF;
  473.                break;
  474.             case 'd':                  /* replace CR LF with LF */
  475.                if ( pOptArg == NULL )
  476.                   pOptArg = "";
  477.                if ( !(*pOptArg == 'u' && *(pOptArg + 1) == '\0')  )
  478.                   Abort("command error, unknown option 'd%s'. Type 'gsar' by itself for help", pOptArg);
  479.                fSearchReplace = 1;
  480.                nItemsSearch = 2;
  481.                SearchBuf[0] = CR;
  482.                SearchBuf[1] = LF;
  483.                nItemsReplace = 1;
  484.                ReplaceBuf[0] = LF;
  485.                break;
  486.             case 'i':
  487.                fFolded = 1;
  488.                break;
  489.             case 'G':
  490.                ShowLicence();
  491.                exit( EXIT_SUCCESS );
  492.                break;
  493.             case 'l':
  494.                Ctrl.fTextual = 0;      /* return to terse display */
  495.                Ctrl.fHex = 0;
  496.                Ctrl.fByteOffset = 0;
  497.                break;
  498.             case 'o':
  499.                fOverWrite = 1;
  500.                break;
  501.             case 'f':
  502.                fForce = 1;
  503.                break;
  504.             case 'F':
  505.                fFilter = 1;
  506.                break;
  507.             case 'B':
  508.                fBuffers = 1;
  509.                break;
  510.             case 'b':
  511.                Ctrl.fByteOffset = 1;    /* display file offset */
  512.                break;
  513.             case 'h':
  514.                Ctrl.fFileSpec = 0;      /* turn off filespec */
  515.                break;
  516.             case 'x':
  517.                Ctrl.fTextual = -1;      /* signal fall through */
  518.                Ctrl.fHex = 1;           /* display context in hex */
  519.             case 'c':
  520.                if ( Ctrl.fTextual == -1 )  /* entered through case 'x' */
  521.                  Ctrl.fTextual = 0;        /* reset sentinel value  */
  522.                else                        
  523.                {
  524.                   Ctrl.fHex = 0;        /* entered through case 'c' */
  525.                   Ctrl.fTextual = 1;    /* display textual context */
  526.                }
  527.  
  528.                if ( pOptArg == NULL )
  529.                   Ctrl.Context = ( Ctrl.fHex == 1 ) ? HEX_CONTEXT : TXT_CONTEXT;
  530.                else
  531.                {
  532.                   longVal = strtol( pOptArg, &pEnd, 10 );
  533.                   if ( *pEnd != '\0' )
  534.                      Abort( "command error, invalid number : %s", pOptArg );
  535.  
  536.                   if ( longVal < 16 || longVal > BUFSIZ )
  537.                      Abort( "command error, context size must be in the range 16 to %d", BUFSIZ );
  538.  
  539.                   if ( longVal > USHRT_MAX )
  540.                      Ctrl.Context = USHRT_MAX;
  541.                   else
  542.                      Ctrl.Context =  (unsigned short) longVal;
  543.                }
  544.                break;
  545.             default:      
  546.                Abort( "internal error, option '%c' not handled in switch", ( unsigned char ) CurOpt );
  547.                break;
  548.          }
  549.       }
  550.    else
  551.    {
  552.       i = 0;
  553.       while (Usage[i] != NULL)
  554.       {
  555.          fputs( Usage[i++], Ctrl.fpMsg);
  556.          fputc( '\n', Ctrl.fpMsg );
  557.       }
  558.       exit( EXIT_SUCCESS );
  559.    }
  560.  
  561.    Ctrl.fVerbose = (  Ctrl.fTextual ||
  562.                       Ctrl.fHex ||
  563.                       Ctrl.fByteOffset ) ? 1 : 0;
  564.  
  565.    return j;
  566. }
  567.  
  568.  
  569.  
  570. /* StreamSearchReplace()
  571.  *                  
  572.  * Perform search or replace using stdin and stdout ie as a 'filter'
  573.  * When gsar operates as a filter all output ie context, byte filenames
  574.  * etc are all sent to stderr.
  575.  */
  576. void StreamSearchReplace( void )
  577. {
  578.    long  nMatches;
  579.  
  580.    Ctrl.pInputFile = "stdin";      /* proper filename   */
  581.    Ctrl.fpMsg = stderr;            /* redirect messages */
  582.  
  583. #ifdef MSDOS
  584.    /* when MSDOS operates on streams it translates characters and terminates
  585.     * input and output when it encounters a CTRL Z. The code below makes
  586.     * MSDOS treat the streams as binary. Stdin cannot be used since MSDOS
  587.     * ignores the CTRL-Z. If you pipe a binary stream to MSDOS under some
  588.     * compilers you might get a write error ( BCC, Zortech )
  589.     */
  590.    if ( isatty( fileno( stdin ) ) )
  591.       Abort("error, input from tty is not supported under MSDOS");
  592.    setmode( fileno(stdin), O_BINARY );
  593.    setmode( fileno(stdout), O_BINARY );
  594. #endif
  595.  
  596.    Ctrl.fpIn = stdin;              /* input from stdin  */
  597.    Ctrl.fpOut = stdout;            /* output to stdout  */
  598.  
  599.    if ( !fSearchReplace )
  600.    {
  601.       nMatches = BMGSearch( &Ctrl);
  602.  
  603.       if ( nMatches > 0 )
  604.         fprintf(Ctrl.fpMsg,"%s : %ld match%s found\n" ,
  605.                 Ctrl.pInputFile,nMatches, ( nMatches == 1 ) ? "" : "es");
  606.    }
  607.    else
  608.    {
  609.       nMatches = BMGSearchReplace( &Ctrl,ReplaceBuf,nItemsReplace );
  610.  
  611.       if ( nMatches == -1 )   /* error in writing file */
  612.          Abort("error in writing file to stdout\n");
  613.       else
  614.          if ( nMatches > 1 )
  615.          {
  616.             fflush(Ctrl.fpOut);
  617.             fprintf(Ctrl.fpMsg,"%s : %ld occurrences changed\n",
  618.                     Ctrl.pInputFile,nMatches);
  619.          }
  620.    }
  621. }
  622.  
  623.  
  624. /* FileSearch()
  625.  *                  
  626.  * Performs a BMG search on one or multiple files. The files
  627.  * to process are found in pFileList. Files that cannot be
  628.  * opened are just ignored.
  629.  */
  630. void FileSearch( void )
  631. {
  632.    long  nMatches;
  633.  
  634.    while ( *pFileList != NULL )
  635.    {
  636.       if (( Ctrl.fpIn = fopen( *pFileList, "rb" )) == NULL )
  637.       {
  638.          fprintf( Ctrl.fpMsg, "gsar: unable to open input file '%s'\n", *pFileList );
  639.          pFileList++;
  640.          continue;
  641.       }
  642.  
  643.       Ctrl.pInputFile = *pFileList;
  644.       Ctrl.fpMsg = stdout;
  645.  
  646. #if defined(MSDOS) && !defined(__ZTC__)
  647.       /* Don't use setvbuf if we're compiling under Zortech
  648.        */
  649.       if ( setvbuf( Ctrl.fpIn, NULL, _IOFBF, INP_BUF_SIZ ) != 0 )
  650.          fprintf( Ctrl.fpMsg, "warning, unable to set up buffering for input file");
  651. #endif
  652.  
  653.       nMatches = BMGSearch( &Ctrl);
  654.       fclose( Ctrl.fpIn );
  655.  
  656.       if ( nMatches > 0 )
  657.          fprintf(Ctrl.fpMsg,"%s : %ld match%s found\n", 
  658.                  *pFileList, nMatches, ( nMatches == 1 ) ? "" : "es");
  659.       pFileList++;
  660.    }
  661. }
  662.  
  663. /* OneSearchReplace()
  664.  *                  
  665.  * Performs a search and replace on a specific outputfile
  666.  */
  667. void OneSearchReplace( void )
  668. {
  669.    long  nMatches = 0;
  670.  
  671.    struct stat StatBuf;    /* struct so we can access file information */
  672.  
  673.    Ctrl.fpMsg = stdout;    /* redirect messages */
  674.    Ctrl.pInputFile = NULL; /* initially we haven't found a file */
  675.  
  676.    /* find the input and output file
  677.     */
  678.    while ( *pFileList != NULL )
  679.    {
  680.       if ( Ctrl.pInputFile == NULL )
  681.          Ctrl.pInputFile = *pFileList;
  682.       else
  683.          if ( pOutFile == NULL )
  684.             pOutFile = *pFileList;
  685.  
  686.       pFileList++;
  687.    }
  688.  
  689.    if (( Ctrl.fpIn = fopen( Ctrl.pInputFile ,"rb" )) == NULL )
  690.       Abort("error, unable to open input file '%s' ", Ctrl.pInputFile );
  691.  
  692.    if (( stat(pOutFile,&StatBuf)) == 0 && !fForce )    /* stat got the info ie. file exists */
  693.       Abort("error, output file '%s' already exists. Use the 'f' option to force overwrite",pOutFile);
  694.  
  695.    if (( Ctrl.fpOut = fopen(pOutFile,"wb")) == NULL )
  696.       Abort("error, unable to open output file '%s' ",pOutFile);
  697.  
  698. #if defined(MSDOS) && !defined(__ZTC__)
  699.    /* Don't use setvbuf if we're compiling under Zortech
  700.     */
  701.    if ( setvbuf( Ctrl.fpIn, NULL, _IOFBF, INP_BUF_SIZ ) != 0 )
  702.       fprintf( Ctrl.fpMsg, "warning, unable to set up buffering for input file");
  703.    if (setvbuf( Ctrl.fpOut, NULL, _IOFBF, INP_BUF_SIZ) != 0 )
  704.       fprintf( Ctrl.fpMsg, "warning, unable to set up buffering for output file");
  705. #endif
  706.  
  707.    nMatches = BMGSearchReplace( &Ctrl,ReplaceBuf,nItemsReplace );
  708.  
  709.    fclose(Ctrl.fpIn);
  710.    fclose(Ctrl.fpOut);
  711.  
  712.    fCriticalPart = 1;      /* ignore interrupts here */
  713.  
  714.    if ( nMatches == -1 )   /* error in writing file */
  715.    {
  716.       fprintf(Ctrl.fpMsg,"gsar: error in writing file '%s' - cleaning up\n",pOutFile);
  717.       if ( remove( pOutFile ) != 0 )
  718.          Abort("error, unable to remove output file '%s'",pOutFile);
  719.       exit( EXIT_FAILURE );  /* exit & tell operating system that we've failed */
  720.    }
  721.  
  722.    if ( nMatches == 0 )
  723.    {
  724.       if ( remove( pOutFile ) != 0 )
  725.          Abort("error, unable to remove output file '%s'",pOutFile);
  726.    }
  727.    else
  728.      fprintf(Ctrl.fpMsg,"%s : %ld occurrences changed\n",Ctrl.pInputFile,nMatches);
  729.  
  730.    /* Make sure the Ctrl-C handler does not delete a completly
  731.     * processed output file
  732.     */
  733.    pOutFile = NULL;      
  734.  
  735.    fCriticalPart = 0;   /* enable interrupts */
  736. }
  737.  
  738.  
  739. /* SearchReplace()
  740.  *                  
  741.  * Performs a BMG search and replace on one or multiple files. The files
  742.  * to process are found in pFileList. Files that cannot be opened
  743.  * are ignored.
  744.  */
  745. void SearchReplace( void )
  746. {
  747.    long  nMatches;
  748.  
  749.    Ctrl.fpMsg = stdout;    /* redirect messages */
  750.    
  751.    while ( *pFileList != NULL )
  752.    {
  753.       Ctrl.pInputFile = *pFileList++;
  754.       if (( Ctrl.fpIn = fopen( Ctrl.pInputFile,"rb")) == NULL )
  755.       {
  756.          fprintf(Ctrl.fpMsg,"gsar: unable to open input file '%s'\n",Ctrl.pInputFile);
  757.          continue;
  758.       }
  759.  
  760.       /* generate a temporary file name with prefix 'gsr_'
  761.          */
  762.       pOutFile = ExtractPathFromFSpec( Ctrl.pInputFile );
  763.       if (( pOutFile = TmpName( pOutFile, "gsr_")) == NULL )
  764.          Abort("error, unable to create a temporary file name");
  765.  
  766.       if (( Ctrl.fpOut = fopen(pOutFile,"wb")) == NULL )
  767.          Abort("error, unable to open output file '%s' ",pOutFile);
  768.  
  769. #if defined(MSDOS) && !defined(__ZTC__)
  770.       /* Don't use setvbuf if we're compiling under Zortech
  771.          */
  772.       if ( setvbuf( Ctrl.fpIn, NULL, _IOFBF, INP_BUF_SIZ ) != 0 )
  773.          fprintf( Ctrl.fpMsg, "warning, unable to set up buffering for input file" );
  774.       if (setvbuf( Ctrl.fpOut, NULL, _IOFBF, INP_BUF_SIZ) != 0 )
  775.          fprintf( Ctrl.fpMsg, "warning, unable to set up buffering for output file");
  776. #endif
  777.  
  778.       nMatches = BMGSearchReplace( &Ctrl,ReplaceBuf, nItemsReplace );
  779.  
  780.       fclose(Ctrl.fpIn);
  781.       fclose(Ctrl.fpOut);
  782.  
  783.       fCriticalPart = 1;      /* ignore interrupts here */
  784.  
  785.       if ( nMatches == -1 )   /* error in writing file */
  786.       {
  787.          fprintf(Ctrl.fpMsg,"gsar: error in writing file '%s' - cleaning up\n",pOutFile);
  788.          if ( remove( pOutFile ) != 0 )
  789.             Abort("error, unable to remove output file '%s'",pOutFile);
  790.          exit( EXIT_FAILURE );
  791.       }
  792.  
  793.       if ( nMatches == 0 )
  794.       {
  795.          if ( remove( pOutFile ) != 0 )
  796.             Abort("error, unable to remove output file '%s'",pOutFile);
  797.       }
  798.       else
  799.       {
  800.          /* if we can't remove the input file delete the tmp output file
  801.             */
  802.          if ( remove( Ctrl.pInputFile ) != 0 )
  803.          {
  804.             fprintf(Ctrl.fpMsg,"gsar: error, unable to remove input file '%s' before rename ( read-only ? )",Ctrl.pInputFile);
  805.             if ( remove( pOutFile ) != 0 )
  806.                Abort("error, unable to remove output file '%s'",pOutFile);
  807.             exit( EXIT_FAILURE );
  808.          }
  809.  
  810.          if ( rename( pOutFile, Ctrl.pInputFile ) != 0 )
  811.                Abort("error, unable to rename file '%s' to '%s'",pOutFile,Ctrl.pInputFile);
  812.  
  813.          fprintf(Ctrl.fpMsg,"%s : %ld occurrences changed\n",Ctrl.pInputFile,nMatches);
  814.       }
  815.       fCriticalPart = 0;
  816.    }
  817. }
  818.  
  819.  
  820. int main( int argc, char *argv[])
  821. {
  822.    int i;            /* number of files on command line */
  823.  
  824.    Ctrl.fFileSpec = 1;
  825.  
  826.    Ctrl.fpMsg = stdout;
  827.  
  828.  
  829.    if( signal( SIGINT,CtrlBreak ) == SIG_ERR )
  830.        Abort("internal error, unable to set SIGINT");
  831.  
  832.    /* parse command line arguments & set variables
  833.     * i is number of files found
  834.     */
  835.    i = GetArgs( argc, argv );
  836.  
  837.    /* perform different health & sanity checks
  838.     */
  839.  
  840.    if ( nItemsSearch == 0 )
  841.       Abort("command error, no search string specified");
  842.  
  843.    /* display search and replace buffers if any
  844.     */
  845.    if ( fBuffers  )
  846.    {
  847.       fputc('\n',Ctrl.fpMsg);
  848.       if ( nItemsSearch > 0 )
  849.       {
  850.          fprintf(Ctrl.fpMsg,"Search buffer  ( ASCII ) : ");
  851.          DumpBuffer( SearchBuf, nItemsSearch, 0 );
  852.          fprintf(Ctrl.fpMsg,"               ( Hex )   : ");
  853.          DumpBuffer( SearchBuf, nItemsSearch, 1 );
  854.          fprintf(Ctrl.fpMsg,"\n");
  855.       }
  856.       if ( nItemsReplace > 0 )
  857.       {
  858.          fprintf(Ctrl.fpMsg,"Replace buffer ( ASCII ) : ");
  859.          DumpBuffer( ReplaceBuf, nItemsReplace, 0 );
  860.          fprintf(Ctrl.fpMsg,"               ( Hex )   : ");
  861.          DumpBuffer( ReplaceBuf, nItemsReplace, 1 );
  862.       }
  863.       return EXIT_SUCCESS;
  864.    }
  865.  
  866.    if ( i == 0 && !fFilter )
  867.       Abort("command error, no input file name specified");
  868.  
  869.    if ( i != 2 && fForce && !fOverWrite )
  870.       Abort("command error, two file names are required to use the 'f' option");
  871.  
  872.    /* set up the search pattern once and for all
  873.     */
  874.    BMGSetup( SearchBuf, ( int ) nItemsSearch, fFolded );
  875.  
  876.    if ( fFilter )
  877.    {
  878.       if ( fOverWrite || fForce )
  879.          Abort("command error, the 'o' or 'f' option is meaningless in 'filter' mode");
  880.  
  881.       StreamSearchReplace();
  882.       return EXIT_SUCCESS;
  883.    }
  884.  
  885.    /* just perform a file search
  886.     */
  887.    if ( !fSearchReplace )
  888.    {
  889.       if ( fOverWrite || fForce )         
  890.          Abort("command error, the 'o' or 'f' option is meaningless in 'search' mode");
  891.  
  892.       FileSearch();
  893.       return EXIT_SUCCESS;
  894.    }
  895.  
  896.    /* Do a search and replace with specific output file
  897.     */
  898.    if ( i == 2 && !fOverWrite && fSearchReplace )
  899.    {
  900.       OneSearchReplace();
  901.       return EXIT_SUCCESS;
  902.    }
  903.  
  904.    if ( !fOverWrite && fSearchReplace )
  905.       Abort("command error, multiple search & replace requires the 'o' option");
  906.  
  907.    if ( fOverWrite && fSearchReplace )
  908.    {
  909.       if ( fForce )         
  910.          Abort("command error, the 'f' option is meaningless in multiple search and replace");
  911.       SearchReplace();
  912.       return EXIT_SUCCESS;
  913.    }
  914.    return EXIT_SUCCESS;
  915. }
  916.  
  917.